package besta.moon;

import java.sql.*;

import besta.moon.statement.MoonStatement;
import besta.moon.sql.Expression;
import besta.moon.sql.expression.ExpressionValue;
import besta.moon.sql.expression.Expressions;

public abstract class MoonCommand {

    public int type;
    public String catalog;
    public String name;

    public MoonResultSet rs;
    public int updateCount = -1;

    public final Expressions columnExpressions;

    public Expressions params = new Expressions();

    public final MoonLogger log;

    public MoonCommand(MoonLogger log) {
        this.log = log;
        this.columnExpressions = new Expressions();
    }

    public MoonCommand(MoonLogger log, Expressions columnExpressions) {
        this.log = log;
        this.columnExpressions = columnExpressions;
    }

    public void addColumnExpression(Expression column) throws SQLException {
        columnExpressions.add(column);
    }

    public void addParameter(ExpressionValue param) {
        params.add(param);
    }

    public void verifyParams() throws SQLException {
        for (int p = 0; p < params.size(); p++) {
            if (((ExpressionValue) params.get(p)).isEmpty()) {
                throw MoonException.create(MoonCode.PARAM_EMPTY, p + 1);
            }
        }
    }

    public void clearParams() {
        for (int p = 0; p < params.size(); p++) {
            ((ExpressionValue) params.get(p)).clear();
        }
    }

    private ExpressionValue getParam(int idx) throws SQLException {
        if (idx < 1 || idx > params.size()) {
            throw MoonException.create(MoonCode.PARAM_IDX_OUT_RANGE, new Object[]{idx, params.size()});
        }
        return ((ExpressionValue) params.get(idx - 1));
    }

    public void setParamValue(int idx, Object value, int dataType) throws SQLException {
        getParam(idx).set(value, dataType);
        if (log.isLogging()) {
            log.println("param" + idx + '=' + value + "; type=" + dataType);
        }
    }

    public void setParamValue(int idx, Object value, int dataType, int length) throws SQLException {
        getParam(idx).set(value, dataType, length);
        if (log.isLogging()) {
            log.println("param" + idx + '=' + value + "; type=" + dataType + "; length=" + length);
        }
    }

    public final void execute(MoonConnection con, MoonStatement st) throws SQLException {
        int savepoint = con.getSavepoint();
        try {
            executeImpl(con, st);
        } catch (Exception e) {
            con.rollback(savepoint);
            throw MoonException.createFromException(e);
        } finally {
            if (con.getAutoCommit()) {
                con.commit();
            }
        }
    }

    public abstract void executeImpl(MoonConnection con, MoonStatement st) throws Exception;

    public MoonResultSet getQueryResult() throws SQLException {
        if (rs == null) {
            throw MoonException.create(MoonCode.RSET_NOT_PRODUCED);
        }
        return rs;
    }

    public MoonResultSet getResultSet() {
        return rs;
    }

    public int getUpdateCount() {
        return updateCount;
    }

    public boolean getMoreResults() {
        rs = null;
        updateCount = -1;
        return false;
    }

    public void setMaxRows(int max) {

    }

    public int getMaxRows() {
        return -1;
    }
}
